package main

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"net"
	"os"
	"strconv"
	"strings"
	"unsafe"

	"google.golang.org/protobuf/proto"
	"test.com/src/msgcmd"
	"test.com/src/protocol"
)

const (
	Head_Len = 4
)

func CreatePackage(cmd uint32, body []byte) []byte {
	msgLen := len(body)
	len := int(unsafe.Sizeof(cmd)) + msgLen

	buf := make([]byte, len+Head_Len)
	data := bytes.NewBuffer(buf)
	data.Reset()

	binary.Write(data, binary.BigEndian, int32(len))
	binary.Write(data, binary.BigEndian, cmd)
	if msgLen > 0 {
		binary.Write(data, binary.BigEndian, body)
	}
	return data.Bytes()
}

func CreateChangeName(name string) []byte {
	msg := &protocol.ChangeNameReq{Name: &name}
	body, _ := proto.Marshal(msg)
	return CreatePackage(msgcmd.ChangeNameReq, body)
}

func CreatePlayerIn(roomId uint32) []byte {
	msg := &protocol.PlayerInReq{RoomId: &roomId}
	body, _ := proto.Marshal(msg)
	return CreatePackage(msgcmd.PlayerInReq, body)
}

func CreateChat(txt string) []byte {
	msg := &protocol.ChatMsg{Txt: &txt}
	body, _ := proto.Marshal(msg)
	return CreatePackage(msgcmd.ChatMsg, body)
}

var conn net.Conn
var myName string
var roomCount int

func main() {
	c, err := net.Dial("tcp", "127.0.0.1:9000")
	if err != nil {
		panic(err)
	}
	conn = c
	defer conn.Close()

	go func() {
		for {

			// response, err := ClientDecode(conn)
			// if err != nil {
			// 	log.Printf("ClientDecode error, %v\n", err)
			// 	break
			// }

			// log.Printf("receive , %v, data:\n", response)

			ClientDecode()

		}
	}()

	select {}
}

func ClientDecode() {

	headData := make([]byte, 4)
	n, _ := io.ReadFull(conn, headData)
	if n != 4 {
		return
	}

	buffer := bytes.NewBuffer(headData)
	var length uint32
	binary.Read(buffer, binary.BigEndian, &length)

	if length < 1 {
		return
	}

	data := make([]byte, length)
	dataNum, _ := io.ReadFull(conn, data)
	if uint32(dataNum) != length {
		return
	}

	handleMsg(data)
}

func handleMsg(data []byte) {
	cmd, buffer := protocol.DecodeCmd(data)
	switch cmd {
	case msgcmd.RoomListMsg:
		handleRoomList(buffer)
	case msgcmd.ChangeNameRsp:
		handleChangeName(buffer)
	case msgcmd.PlayerInRsp:
		handleRoomIn(buffer)
	case msgcmd.ChatMsg:
		handleChat(buffer)
	}
}

func handleRoomList(buffer *bytes.Buffer) {
	msg := protocol.DecodeRoomList(buffer)
	roomCount = int(*msg.Count)

	printRoom()

	go inputName("请输入您的名字:")
}

func printRoom() {
	var b strings.Builder
	for i := 0; i < int(roomCount); i++ {
		b.WriteString(strconv.Itoa(i))
		b.WriteString(" ")
	}
	fmt.Println("房间:", b.String())
}

func inputName(tips string) {
	fmt.Println(tips)

	scanner := bufio.NewScanner(os.Stdin)
	scanner.Scan()
	line := scanner.Text()

	msg := CreateChangeName(line)
	conn.Write(msg)
}

func handleChangeName(buffer *bytes.Buffer) {
	msg := protocol.DecodeChangeNameRsp(buffer)
	if *msg.Result {
		myName = *msg.Name
		go inputRoomId()

	} else {
		if msg.ErrMsg != nil {
			fmt.Println(*msg.ErrMsg)
		}
		go inputName("请换一个名字:")
	}
}

func inputRoomId() {
	fmt.Println("请输入房间号:")
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		line := scanner.Text()
		roomId, _ := strconv.Atoi(line)
		if roomId < 0 || roomId > roomCount {
			printRoom()
			fmt.Println("请输入正确的房间号")
			continue
		}

		msg := CreatePlayerIn(uint32(roomId))
		conn.Write(msg)
		break
	}
}

func handleRoomIn(buffer *bytes.Buffer) {
	msg := protocol.DecodePlayerInRsp(buffer)
	if msg.Msgs != nil {
		for _, val := range msg.Msgs {
			fmt.Println(val)
		}
	}
	if *msg.Result {
		fmt.Println("请输入你想说的话:")

		go inputChat()
	}
}

func inputChat() {
	scanner := bufio.NewScanner(os.Stdin)

	for scanner.Scan() {

		line := scanner.Text()

		msg := CreateChat(line)
		conn.Write(msg)
	}
}

func handleChat(buffer *bytes.Buffer) {
	msg := protocol.DecodeChat(buffer)
	if *msg.Sender == myName {
		fmt.Println("你", "说:", *msg.Txt)
	} else {
		fmt.Println(*msg.Sender, "说:", *msg.Txt)
	}
}
